﻿using GetSize.Properties;
using QueryEngine;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using UsnOperation;
using Timer = System.Threading.Timer;

namespace GetSize
{
    public partial class MainForm : Form
    {
        private BackgroundWorker scanWorker;
        private BackgroundWorker saveWorker;
        private BackgroundWorker loadWorker;
        private BackgroundWorker treeWorker;
        private readonly object locker;
        private List<FileAndDirectoryEntry> DataList;
        private Dictionary<string, Entry> FileTreeCache;
        private int lastFillCount;
        private int fillCount;
        // 默认的线程数量，与处理器核心数量一致
        private double FILL_THREAD_COUNT = Environment.ProcessorCount;
        /// <summary>
        /// 单位为秒
        /// </summary>
        private const int TIMER_INTERVAL = 1;
        private readonly Timer fillTextUpdateTimer;

        private bool IsBusy;

        private string StatusText
        {
            set
            {
                Call(() =>
                {
                    statusLabel.Text = $"[{DateTime.Now.ToString("HH:mm:ss")}] {value}";
                });
            }
        }
        public MainForm()
        {
            InitializeComponent();

            DoubleBuffered = true;

            DataList = new List<FileAndDirectoryEntry>();
            FileTreeCache = new Dictionary<string, Entry>();

            treeView.ImageList = new ImageList();
            treeView.ImageList.Images.Add("folder", Resources.folder);
            treeView.ImageList.Images.Add("file", Resources.file);
            treeView.ImageList.Images.Add("drive", Resources.drive);

            locker = new object();
            fillTextUpdateTimer = new Timer(state =>
            {
                var speed = Math.Round((fillCount - lastFillCount) * 1f / TIMER_INTERVAL);
                var percentage = fillCount * 1.0 / DataList.Count;
                StatusText = $"构建文件树 {fillCount}/{DataList.Count}({percentage.ToString("0.00%")}) {speed} /秒";
                lastFillCount = fillCount;
            }, null, Timeout.Infinite, Timeout.Infinite);
        }

        private void Call(Action action)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MethodInvoker(action));
            }
            else
            {
                action();
            }
        }

        private void AddTreeNode(string path, bool isDirectory)
        {
            var pathStack = new Stack<string>();
            pathStack.Push(path);

            Entry parentNode = null;
            string parentPath = path;

            while (true)
            {
                parentPath = Path.GetDirectoryName(parentPath);
                lock (locker)
                {
                    if (FileTreeCache.ContainsKey(parentPath))
                    {
                        parentNode = FileTreeCache[parentPath];
                        break;
                    }
                }

                pathStack.Push(parentPath);
            }

            while (pathStack.Count > 0)
            {
                var currentPath = pathStack.Pop();
                var currentNode = new Entry(currentPath)
                {
                    IsDirectory = isDirectory
                };
                currentNode.GetFileSize();
                lock (locker)
                {
                    if (!FileTreeCache.ContainsKey(currentPath))
                    {
                        FileTreeCache.Add(currentPath, currentNode);
                    }
                }
                parentNode.Children.Add(currentNode);
                parentNode = currentNode;
            }
        }
        private TreeNode AddTreeNode(TreeView parent, string text)
        {
            var newNode = new TreeNode(text);
            Call(() =>
            {
                parent.Nodes.Add(newNode);
            });
            return newNode;
        }

        private void scanToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (IsBusy)
            {
                return;
            }
            if (scanWorker == null)
            {
                scanWorker = new BackgroundWorker { WorkerSupportsCancellation = true };
                scanWorker.DoWork += Worker_DoWork;
            }
            scanWorker.RunWorkerAsync();
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            FileTreeCache.Clear();
            treeView.Nodes.Clear();
            IsBusy = true;
            StatusText = "正在扫描...";
            var start = DateTime.Now;

            var fixedNtfsDrives = Engine.GetAllFixedNtfsDrives().ToArray();

            var count = 0;

            foreach (var drive in fixedNtfsDrives)
            {
                new Thread(() =>
                {
                    var nodeText = string.Format("{0} ({1})", drive.VolumeLabel, drive.Name);
                    var node = AddTreeNode(treeView, nodeText + " * 正在扫描...");
                    Call(() =>
                    {
                        node.Tag = drive.Name;
                        node.ImageKey = "drive";
                    });

                    var entry = new Entry(drive.Name) { IsDirectory = true };
                    FileTreeCache.Add(drive.Name, entry);

                    var usnOperator = new UsnOperator(drive);

                    var usnEntries = usnOperator.GetEntries();

                    var folders = usnEntries.Where(_ => _.IsFolder).ToArray();
                    List<FrnFilePath> paths = Engine.GetFolderPath(folders, drive);

                    DataList.AddRange(usnEntries.Join(
                        paths,
                        usn => usn.ParentFileReferenceNumber,
                        path => path.FileReferenceNumber,
                        (usn, path) => new FileAndDirectoryEntry(usn, path.Path)));

                    Call(() =>
                    {
                        node.Text = nodeText;
                    });

                    lock (locker)
                    {
                        count++;

                        if (count < fixedNtfsDrives.Length)
                        {
                            return;
                        }
                    }

                    ScanComplete(start);
                })
                { IsBackground = true }.Start();
            }

        }

        private void ScanComplete(DateTime start)
        {
            var end = DateTime.Now;
            StatusText = $"扫描完成，耗时 {end - start}, 开始构建文件树...";

            fillTextUpdateTimer.Change(0, TIMER_INTERVAL * 1000);
            fillCount = 0;

            var dirCount = 0;
            var fileCount = 0;

            var count = 0L;
            var size = (int)Math.Ceiling(DataList.Count / FILL_THREAD_COUNT);

            for (int i = 0; i < FILL_THREAD_COUNT; i++)
            {
                DoFill(i, size, (dc, fc) =>
                {
                    dirCount += dc;
                    fileCount += fc;

                    lock (locker)
                    {
                        count++;
                        if (count < FILL_THREAD_COUNT)
                        {
                            return;
                        }
                    }
                    var end2 = DateTime.Now;

                    fillTextUpdateTimer.Change(Timeout.Infinite, Timeout.Infinite);

                    StatusText = $"填充耗时 {end2 - end}, 共耗时 {end2 - start}, 发现 {dirCount} 文件夹, {fileCount} 文件";
                    IsBusy = false;
                });
            }

        }

        private void DoFill(int index, int size,
            Action<int, int> callback)
        {
            new Thread(() =>
            {
                var dirCount = 0;
                var fileCount = 0;
                foreach (var entry in DataList.Skip(index * size).Take(size))
                {
                    if (entry.IsFolder)
                    {
                        dirCount++;
                    }
                    else
                    {
                        fileCount++;
                    }
                    AddTreeNode(entry.FullFileName, entry.IsFolder);
                    lock (locker)
                    {
                        fillCount++;
                    }
                }
                callback(dirCount, fileCount);
            })
            { IsBackground = true }.Start();
        }

        private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (IsBusy)
            {
                return;
            }
            if (treeWorker == null)
            {
                treeWorker = new BackgroundWorker();
                treeWorker.DoWork += TreeWorker_DoWork;
            }
            //if (MessageBox.Show("占用空间大小计算会消耗较长时间，需要足够的耐心等待，是否继续？", "提示",
            //    MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
            //{
            //    return;
            //}
            IsBusy = true;
            treeWorker.RunWorkerAsync();
        }

        private void TreeWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            TreeNode node = null;
            Call(() =>
            {
                node = treeView.SelectedNode;
            });
            if (node == null || node.Nodes.Count > 0)
            {
                StatusText = "未选中目录";
                IsBusy = false;
                return;
            }
            string path = null;
            Call(() =>
            {
                path = node.Tag.ToString();
            });

            if (path == null || !FileTreeCache.ContainsKey(path))
            {
                StatusText = $"路径 {path} 不存在";
                IsBusy = false;
                return;
            }
            var entry = FileTreeCache[path];
            StatusText = "正在计算空间占用情况...";

            //var pool = new ThreadPool(node);
            //pool.OnBeforeNodeScan += Pool_OnBeforeNodeScan;
            //pool.OnNodeScanCompleted += Pool_OnNodeScanCompleted;
            //pool.OnAllScanCompleted += Pool_OnAllScanCompleted;
            //pool.Run(entry);

            var nodes = new List<TreeNode>(entry.Children.Count);
            
            StatusText = "正在收集文件大小..";
            Task.WaitAll(Task.WhenAll(entry.Children.Select(item =>
            {
                return Task.Run(() =>
                {
                    item.GetSize();
                });
            })));

            StatusText = "正在填充节点文件大小..";
            foreach (var item in entry.Children.OrderByDescending(_ => _.GetSize()))
            {
                var name = Path.GetFileName(item.FullName);
                var size = Util.FormatSize(item.GetSize());
                var text = name + " " + (item.IsDirectory ? $"({item.GetChildrenCount()} 个子项, 占用 {size})" : size);
                nodes.Add(new TreeNode(text)
                {
                    Tag = item.FullName,
                    ImageKey = item.IsDirectory ? "folder" : "file"
                });
            }

            Call(() =>
            {
                node.Nodes.AddRange(nodes.ToArray());
                node.ExpandAll();
            });
            StatusText = "计算完成";
            IsBusy = false;
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveWorker == null)
            {
                saveWorker = new BackgroundWorker();
                saveWorker.DoWork += SaveWorker_DoWork;
            }
            IsBusy = true;
            saveWorker.RunWorkerAsync();
        }

        private void SaveWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            StatusText = "正在保存数据...";

            var lp = "0.00%";

            Storage.Set(FileTreeCache.Count, FileTreeCache.Select(pair => pair.Value).OrderBy(entry => entry.FullName), progress =>
            {
                var p = progress.ToString("0.00%");
                if (p != lp)
                {
                    StatusText = $"正在保存数据 {p}";
                    lp = p;
                }
            });

            StatusText = "保存完成";
            IsBusy = false;
        }

        private void loadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (loadWorker == null)
            {
                loadWorker = new BackgroundWorker();
                loadWorker.DoWork += LoadWorker_DoWork;
            }
            IsBusy = true;
            loadWorker.RunWorkerAsync();
        }

        private void LoadWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            StatusText = "正在加载数据...";

            var fixedNtfsDrives = Engine.GetAllFixedNtfsDrives().ToArray();
            foreach (var drive in fixedNtfsDrives)
            {
                var node = AddTreeNode(treeView, string.Format("{0} ({1})", drive.VolumeLabel, drive.Name));
                node.Tag = drive.Name;
                node.ImageKey = "drive";
            }

            var lp = "0.00%";

            Storage.Get((progress, entry) =>
            {
                var p = progress.ToString("0.00%");
                if (p != lp)
                {
                    StatusText = $"正在加载数据 {p}";
                    lp = p;
                }
                if (!FileTreeCache.ContainsKey(entry.FullName))
                {
                    FileTreeCache[entry.FullName] = entry;
                }
                var parentPath = Path.GetDirectoryName(entry.FullName);

                if (parentPath != null)
                {
                    FileTreeCache[parentPath].Children.Add(entry);
                }
            });

            StatusText = "加载完成";
            IsBusy = false;
        }
    }
}
